"""
================================================================================
                     ------------utf-8--------------
================================================================================
@Author: rfdsg
@Create Time: 2023/11/4 - 10:42
@Description:
@Attention:
"""
import graphviz
import lightgbm as lgb
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from scipy import interpolate
from sklearn.metrics import classification_report, confusion_matrix, roc_curve, roc_auc_score
from sklearn.model_selection import cross_val_score, StratifiedKFold, RandomizedSearchCV
from scipy.stats import randint as sp_randint
from sklearn.preprocessing import label_binarize


def griddata(x_data: pd.Series=None, y_data: pd.Series=None,
             z_data: np.ndarray=None, target_data=None):
    """

    :param x_data: 原始数据点集的x向量
    :param y_data: 原始数据点集的x向量
    :param z_data: 原始数据的
    :param target_data:
    :return:
    """
    # 通过 meshgrid 创建网格
    z_in = interpolate.griddata((x_data, y_data), z_data, target_data, method='cubic')
    return z_in


# 数据读取
base_train = pd.read_pickle("基准1.pickle")
base_test = pd.read_pickle("基准2.pickle")
terrain_train = pd.read_pickle("height_train.pickle")
terrain_test = pd.read_pickle("height_test.pickle")
mss_train = pd.read_pickle("mss_sio_32.1_train.pickle")
mss_test = pd.read_pickle("mss_sio_32.1_test.pickle")



"""
=======================================================================================
                                    基准数据的分类和处理
=======================================================================================
"""
# 生成一个空的分类列
base_train['分类'] = pd.Series()

# 设定分类条件并赋值
base_train.loc[base_train['重力异常值'] < 0, '分类'] = 0
base_train.loc[(base_train['重力异常值'] >= 0) & (base_train['重力异常值'] < 40), '分类'] = 1
base_train.loc[(base_train['重力异常值'] >= 40) & (base_train['重力异常值'] < 80), '分类'] = 2
base_train.loc[(base_train['重力异常值'] >= 80) & (base_train['重力异常值'] < 140), '分类'] = 3
print(base_train['分类'].value_counts())
"""
                           测试数据
============================================================
"""
# 生成一个空的分类列
base_test['分类'] = pd.Series()

# 设定分类条件并赋值
base_test.loc[base_test['重力异常值'] < 0, '分类'] = 0
base_test.loc[(base_test['重力异常值'] >= 0) & (base_test['重力异常值'] < 40), '分类'] = 1
base_test.loc[(base_test['重力异常值'] >= 40) & (base_test['重力异常值'] < 80), '分类'] = 2
base_test.loc[(base_test['重力异常值'] >= 80) & (base_test['重力异常值'] < 140), '分类'] = 3
print(base_test['分类'].value_counts())
#
base_x = np.sort(base_train['纬度'].unique())
base_y = np.sort(base_train['经度'].unique())
base_x, base_y = np.meshgrid(base_x, base_y)
target_train_grid = (base_x, base_y)




"""
=======================================================================================
                                    训练数据集处理
=======================================================================================
"""
# 地形训练数据集插值展开
terrain_train = griddata(terrain_train.lat, terrain_train.lon, terrain_train.z, target_train_grid)
terrain_train = terrain_train.flatten()
terrain_train = pd.Series(terrain_train, name='地形')
# mss训练数据集插值展开
mss_train = griddata(mss_train.lat, mss_train.lon, mss_train.z, target_train_grid)
mss_train = mss_train.flatten()
mss_train = pd.Series(mss_train, name='mss')
"""
=================================================================
"""
#
base_x = np.sort(base_test['纬度'].unique())
base_y = np.sort(base_test['经度'].unique())
base_x, base_y = np.meshgrid(base_x, base_y)
target_test_grid = (base_x, base_y)
# 地形测试数据集
terrain_test = griddata(terrain_test.lat, terrain_test.lon, terrain_test.z, target_test_grid)
terrain_test = terrain_test.flatten()
terrain_test = pd.Series(terrain_test, name='地形')
# mss测试数据集
mss_test = griddata(mss_test.lat, mss_test.lon, mss_test.z, target_test_grid)
mss_test = mss_test.flatten()
mss_test = pd.Series(mss_test, name='mss')
# 预处理
X_train = base_train.iloc[:, 0:2]
X_train['地形'] = terrain_train
X_train['mss'] = mss_train
X_train.columns = ['lon', 'lat', 'height', 'mss']
X_test = base_test.iloc[:, 0:2]
X_test['地形'] = terrain_test
X_test['mss'] = mss_test
X_test.columns = ['lon', 'lat', 'height', 'mss']
# y的数据
y_train = base_train.loc[:, '分类'].astype(int)
y_test = base_test.loc[:, '分类'].astype(int)
# 定义参数分布
param = {
    'num_leaves': sp_randint(20, 50),  # 示例参数分布，您可以根据需求添加更多参数
    'max_depth': sp_randint(2, 15),
    'learning_rate': [0.1, 0.2, 0.3, 0.4, 0.5, 0.05, 0.02, 0.04, 0.03]
}

# 创建模型
model = lgb.LGBMClassifier(objective='multiclass', num_class=4, boosting_type='gbdt', is_unbalance=True)

# 使用随机搜索进行自动调参
random_search = RandomizedSearchCV(model, param_distributions=param, n_iter=10, cv=5, scoring='accuracy')
random_search.fit(X_train, y_train)

# 输出最佳参数组合
print("最佳参数组合:", random_search.best_params_)
# 最佳模型
best_model = random_search.best_estimator_

# 训练最佳模型
best_model.fit(X_train, y_train)

# 用最佳模型对测试集进行预测
y_pred = best_model.predict(X_test)

# 预测概率
y_pred_prob = best_model.predict_proba(X_test)

# 定义交叉验证方式，例如 Stratified K-Fold
kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# 进行交叉验证评估
scores = cross_val_score(best_model, X_train, y_train, cv=kfold, scoring='accuracy')
# 输出交叉验证的准确率
print('交叉验证准确率:', scores)
print('平均准确率:', scores.mean())
# 输出分类报告
print("分类报告:")
print(classification_report(y_test, y_pred))
# 输出混淆矩阵
print("混淆矩阵:")
print(confusion_matrix(y_test, y_pred))
# 绘图
## 将多分类标签二值化
y_test_binarized = label_binarize(y_test, classes=best_model.classes_)

# 计算每个类别的 ROC 曲线和 AUC 值
fpr = dict()
tpr = dict()
roc_auc = dict()

n_classes = len(best_model.classes_)
for i in range(n_classes):
    fpr[i], tpr[i], _ = roc_curve(y_test_binarized[:, i], y_pred_prob[:, i])
    roc_auc[i] = roc_auc_score(y_test_binarized[:, i], y_pred_prob[:, i])

# 绘制多类别的 ROC 曲线
plt.figure()
colors = ['aqua', 'darkorange', 'cornflowerblue', 'green']
class_names = ['lon', 'lat', 'height', 'mss']  # 类名列表
for i, color in zip(range(n_classes), colors):
    plt.plot(fpr[i], tpr[i], color=color, lw=2, label=f'ROC curve (area = {roc_auc[i]:.2f}) for {class_names[i]}')

plt.plot([0, 1], [0, 1], 'k--', lw=2)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic (ROC) for multi-class')
plt.legend(loc='lower right')
plt.savefig('ROC.jpg', dpi=300)
plt.show()
page = lgb.plot_tree(best_model)

